package com.slipper.common.util;

import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.servlet.http.HttpServletResponse;

import org.apache.tomcat.util.http.fileupload.IOUtils;

import com.slipper.common.constants.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;

public final class FileUtil {
    private final static Logger logger = LoggerFactory.getLogger(FileUtil.class);
    
    /**
     * 下载文件
     * @param response HttpServletResponse
     * @param fileName 输出的文件名
     * @param realPath 文件所在路径
     */
    public static void downloadFile(
            HttpServletResponse response,
            String fileName,
            String realPath) {
        BufferedInputStream is = null;
        OutputStream os = null;
        String result = null;
        try {
            if (fileName == null || fileName.trim().isEmpty()) {
                fileName = "default"; // 定义保存的文件名字
            }
            String trueName = URLEncoder.encode(fileName, Constants.UTF_8);
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment; filename=\"" + trueName + "\"");
            
            is = new BufferedInputStream(Files.newInputStream(Paths.get(realPath)));
            os = response.getOutputStream();
            byte[] b = new byte[2048];
            int len;
            while ((len = is.read(b)) > 0) {
                os.write(b, 0, len);
            }
        } catch (IOException ex) {
            result = "用户取消下载或其它IO异常" + ex.getMessage();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException ex) {
                    result = "关闭输入流异常：" + ex.getMessage();
                }
            }
            if (os != null) {
                try {
                    os.flush();
                } catch (IOException ex) {
                    result = "清空输出缓冲异常：" + ex.getMessage();
                }
                try {
                    os.close();
                } catch (IOException ex) {
                    result = "关闭输出流异常：" + ex.getMessage();
                }
            }
        }
        if (result != null) {
            logger.error(result);
        }
    }

    /**
     * 下载模板
     * @param response HttpServletResponse
     * @return null 或者是 错误描述
     */
    public static String downloadTpl(
            HttpServletResponse response,
            String tplFileName,
            String fileName) {
        return downloadTpl(response, tplFileName, fileName, "application/octet-stream");
    }

    /**
     * 下载模板
     * @param response HttpServletResponse
     * @return null 或者是 错误描述
     */
    public static String downloadTpl(
            HttpServletResponse response,
            String tplFileName,
            String fileName,
            String contentType) {
        BufferedInputStream is = null;
        OutputStream os = null;
        String result = null;
        try {
            if (fileName == null || fileName.trim().isEmpty()) {
                fileName = "default"; // 定义保存的文件名字
            }
            String trueName = URLEncoder.encode(fileName, Constants.UTF_8);
            response.setContentType(contentType);
            response.addHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_TYPE);
            response.addHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION);
            response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + trueName);

            is = new BufferedInputStream(Objects.requireNonNull(FileUtil.class
                    .getClassLoader()
                    .getResourceAsStream("template/" + tplFileName)));
            os = response.getOutputStream();
            byte[] b = new byte[2048];
            int len;
            while ((len = is.read(b)) > 0) {
                os.write(b, 0, len);
            }
        } catch (IOException ex) {
            logger.error("", ex);
            // 下载过程中用户取消或其它IO异常
            result = "用户取消下载或其它IO异常" + ex.getMessage();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException ex) {
                    result = "关闭输入流异常：" + ex.getMessage();
                }
            }
            if (os != null) {
                try {
                    os.flush();
                } catch (IOException ex) {
                    result = "清空输出缓冲异常：" + ex.getMessage();
                }
                try {
                    os.close();
                } catch (IOException ex) {
                    result = "关闭输出流异常：" + ex.getMessage();
                }
            }
        }

        if (result != null) {
            logger.error(result);
        }
        return result;
    }
    
    /**
    *
    * @param fileList 每个文件的字节流
    * @param fileNameList 每个文件的文件名
    */
   public static void downloadZip(HttpServletResponse response, String zipFileName, 
           List<String> fileList, List<String> fileNameList) throws IOException {
        response.setContentType("application/octet-stream");
        String trueName = URLEncoder.encode(zipFileName, Constants.UTF_8);
        response.setHeader("Content-Disposition", "attachment; filename=\"" + trueName + "\"");

        ByteArrayOutputStream output = new ByteArrayOutputStream();
        CheckedOutputStream cos = new CheckedOutputStream(output, new CRC32());
        ZipOutputStream zos = new ZipOutputStream(cos);

        Set<String> fileNameSet = new HashSet<>();
        for (int i = 0; i < fileList.size(); i++) {
            String file = fileList.get(i);
            String fileName = fileNameList.get(i);
            
            int suffix = 1;
            while(!fileNameSet.add(fileName)) {
                fileName = fileNameList.get(i) + "_" + suffix;
                suffix++;
            }

            //构建输入流
            BufferedInputStream bis = new BufferedInputStream(Files.newInputStream(Paths.get(file)));
            //创建文件（zip里面的文件）
            ZipEntry entry = new ZipEntry(fileName);
            //放入文件
            zos.putNextEntry(entry);
            //写入文件
            IOUtils.copy(bis, zos);
            //关闭流
            bis.close();
        }

        zos.closeEntry();
        zos.close();

        //设置返回信息
        response.setHeader("Content-Length", String.valueOf(output.size()));
        IOUtils.copy(new ByteArrayInputStream(output.toByteArray()), response.getOutputStream());

        //创建完压缩文件后关闭流
        cos.close();
        output.close();
   }
    
    /*
     * 获取文件扩展名
     */
    public static String getExtension(String filename) {
        if ((filename != null) && !filename.isEmpty()) {
            int i = filename.lastIndexOf('.');
            if ((i > -1) && (i < (filename.length() - 1))) {
                return filename.substring(i + 1);
            }
        }
        return "";
    }
    
    public static synchronized String getFileCode() {
        return (new SimpleDateFormat("yyyyMMddhhmmss")).format(new Date()) + Tool.getRandomString(6);
    }
    
    public static String getFileName() {
        return FileUtil.getFileCode();
    }
    
    /**
     * 获取文件路径
     * @param relativePath 文件相对路径
     * @param pathMode 文件路径模式
     */
    public static String getFilePath(String relativePath, String pathMode) {
        StringBuilder filePath = new StringBuilder(relativePath);
        Date curDate = new Date();
        switch (Integer.parseInt(pathMode)) {
            case 0: // 无策略 0
                break;
            // 每月
            case 2: // 每日
                filePath.append((new SimpleDateFormat("yyyyMMdd")).format(curDate)).append(File.separatorChar);
                break;
            case 3: // 每年
                filePath.append((new SimpleDateFormat("yyyy")).format(curDate)).append(File.separatorChar);
                break;
            default:
                filePath.append((new SimpleDateFormat("yyyyMM")).format(curDate)).append(File.separatorChar);
                break;
        }
        return filePath.toString();
    }
}
